package lxs.swift.instance;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.time.temporal.Temporal;
import java.util.*;

public class Types {
    public static boolean isBase(Object o) {
        return o instanceof Number || o instanceof CharSequence || o instanceof Character
                || o instanceof Boolean || o instanceof Date || o instanceof Temporal;
    }

    public static boolean isInt(Object o) {
        if (o instanceof Number) {
            if (o instanceof Float) return false;
            if (o instanceof Double) return false;
            return !(o instanceof BigDecimal);
        }
        return false;
    }

    public static boolean isObject(Object o) {
        if (o instanceof Map) return true;
        return !isBase(o) && !isArray(o);
    }

    public static boolean isArray(Object o) {
        if (o instanceof Collection) return true;
        return o.getClass().isArray();
    }

    public static HashStruct toStruct(Object o) {
        if (o == null) return null;
        if (o instanceof HashStruct) return (HashStruct) o;
        Map<String, Object> map = new HashMap<>();
        if (o instanceof Map) {
            Map<?, ?> mapTemp = (Map<?, ?>) o;
            mapTemp.forEach((k, v) -> {
                Object structuring = structuring(v);
                if (k == null) map.put(null, structuring);
                else map.put(k.toString(), structuring);
            });
            return new HashStruct(map);
        }
        if (!isBase(o) && !isArray(o)) {
            Class<?> aClass = o.getClass();
            while (aClass != Object.class) {
                for (Field field : aClass.getDeclaredFields()) {
                    if (Modifier.isStatic(field.getModifiers())) continue;
                    if (Modifier.isTransient(field.getModifiers())) continue;
                    field.setAccessible(true);
                    String name = field.getName();
                    Object value;
                    try {
                        value = field.get(o);
                    } catch (Exception e) {
                        value = null;
                    }
                    Object structuring = structuring(value);
                    map.put(name, structuring);
                }
                aClass = aClass.getSuperclass();
            }
            return new HashStruct(map);
        }
        throw new RuntimeException(o.getClass().getName() + " cannot to convert to HashStruct");
    }

    public static StructArray toArray(Object o) {
        if (o == null) return null;
        if (o instanceof StructArray) return ((StructArray) o);
        if (o instanceof Collection) {
            Collection<?> collection = (Collection<?>) o;
            return new StructArray(new ArrayList<>(collection));
        }
        if (o.getClass().isArray()) {
            return new StructArray(Arrays.asList(((Object[]) o)));
        }
        throw new RuntimeException(o.getClass().getName() + " cannot to convert to StructArray");
    }

    public static Object structuring(Object o) {
        if (o == null) return null;
        if (Types.isBase(o)) return o;
        if (o.getClass().isEnum()) return ((Enum<?>) o).name();
        if (Types.isObject(o)) return Types.toStruct(o);
        return Types.toArray(o);
    }
}
